/*
     This file is part of libmicrohttpd
     Copyright (C) 2007 Christian Grothoff (and other contributing authors)

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     License as published by the Free Software Foundation; either
     version 2.1 of the License, or (at your option) any later version.

     This library is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Lesser General Public License for more details.

     You should have received a copy of the GNU Lesser General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
/**
 * @file minimal_example.c
 * @brief minimal example for how to use libmicrohttpd
 * @author Christian Grothoff
 */

//#include "platform.h"
#include <stdio.h>
#include <stdlib.h>
#include <microhttpd.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>

#include <string>
#include <fstream>
#include <sstream>

#include <unistd.h>
using namespace std;

static const char* gUrlRootPath = "/";
static const char* gLabelConf = "conf";
static const char* gLabelRtsp = "rtsp";
static const char* gLabelUrls = "urls";
static const char* gXMLVersion = "1.0";
static const char* gMethodGet = "GET";
static const char* gMethodPost = "POST";


static char* gTsUrl = NULL;

xmlXPathObjectPtr
getnodeset (xmlDocPtr doc, xmlChar *xpath){

	xmlXPathContextPtr context;
	xmlXPathObjectPtr result;

	context = xmlXPathNewContext(doc);
	if (context == NULL) {
		printf("Error in xmlXPathNewContext\n");
		return NULL;
	}
	result = xmlXPathEvalExpression(xpath, context);
	xmlXPathFreeContext(context);
	if (result == NULL) {
		printf("Error in xmlXPathEvalExpression\n");
		return NULL;
	}
	if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
		xmlXPathFreeObject(result);
                printf("No result\n");
		return NULL;
	}
	return result;
}

const char* adjUrl(const char * url, const char * label)
{
	if('\0' == *url)
	{
		url += 0;
	}
	else if(0 == strncmp(url, label, strlen(label)))
	{
		url += strlen(label);
	}
	else
	{
		return NULL;
	}

	if('/' == *url) url++;
	return url;
}

xmlNodePtr genLable_url(const char * url, const char * name)
{
	xmlNodePtr node, nUrlData;

	node = xmlNewNode(NULL, BAD_CAST "url");

	if(0 == strcmp("ts", name))
	{
		xmlNewProp(node, BAD_CAST "id", BAD_CAST "ts");

		nUrlData = xmlNewNode(NULL, BAD_CAST "description");
		xmlAddChild(node, nUrlData);
		xmlNodeSetContent(nUrlData, BAD_CAST "use ts mux");

		nUrlData = xmlNewNode(NULL, BAD_CAST "addr");
		xmlAddChild(node, nUrlData);
		xmlNodeSetContent(nUrlData, BAD_CAST gTsUrl);
	}
	else
	{
		xmlNewProp(node, BAD_CAST "id", BAD_CAST "raw");

		nUrlData = xmlNewNode(NULL, BAD_CAST "description");
		xmlAddChild(node, nUrlData);
		xmlNodeSetContent(nUrlData, BAD_CAST "use raw encode data");

		nUrlData = xmlNewNode(NULL, BAD_CAST "addr");
		xmlAddChild(node, nUrlData);
		xmlNodeSetContent(nUrlData, BAD_CAST "rtsp://192.168.1.66/raw");
	}

	return node;
}

xmlNodePtr genLabel_urls(const char * url)
{
	url = adjUrl(url, gLabelUrls);
	if(NULL == url) return NULL;

	xmlNodePtr node, n0;
	bool bReqValid = false;

	node = xmlNewNode(NULL, BAD_CAST gLabelUrls);

	if('\0' == *url)
	{
		bReqValid = true;
		xmlAddChild(node, genLable_url(url, "ts"));
		xmlAddChild(node, genLable_url(url, "raw"));
	}
	else if(0 == strncmp(url, "ts", strlen("ts")))
	{
		n0 = genLable_url(url, "ts");

		if(n0)
		{
			bReqValid = true;
			xmlAddChild(node, n0);
		}
	}
	else if(0 == strncmp(url, "raw", strlen("raw"))	)
	{
		n0 = genLable_url(url, "raw");

		if(n0)
		{
			bReqValid = true;
			xmlAddChild(node, n0);
		}
	}

	if(!bReqValid)
	{
		xmlFreeNode(node);
		return NULL;
	}
	else
	{
		return node;
	}

}

int parseLabel_url(const char* url, const char * name, xmlDocPtr doc)
{
	if(0 == strncmp(name, "ts", strlen("ts")))
	{
		///
		xmlXPathObjectPtr result;
		xmlNodeSetPtr nodeset;
		int i;
		xmlChar *keyword;

		result = getnodeset (doc, (xmlChar*)"/conf/rtsp/urls/url[@id='ts']/addr");
		if(result)
		{
			nodeset = result->nodesetval;
			for (i=0; i < nodeset->nodeNr; i++) {
				keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1);
//				printf("keyword: %s\n", keyword);
				strcpy(gTsUrl, (const char *)keyword);
				xmlFree(keyword);
			}
			xmlXPathFreeObject (result);

			return 0;
		}
	}

	return -1;
}

int parseLabel_urls(const char* url, xmlDocPtr doc)
{
	url = adjUrl(url, gLabelUrls);
	if(NULL == url) return -1;

	bool bReqValid = false;
	if(0 == strncmp(url, "ts", strlen("ts")))
	{
		if(0 == parseLabel_url(url, "ts", doc))
		{
			bReqValid = true;
		}
	}
	else if(0 == strncmp(url, "raw", strlen("raw")))
	{
		if(0 == parseLabel_url(url, "raw", doc))
		{
			bReqValid = true;
		}
	}

	if(bReqValid)
	{
		return 0;
	}

	return -1;
}

xmlNodePtr genLabel_rtsp(const char * url)
{
	url = adjUrl(url, gLabelRtsp);
	if(NULL == url) return NULL;

	xmlNodePtr node, n0;
	bool bReqValid = false;

	node = xmlNewNode(NULL, BAD_CAST gLabelRtsp);
	n0 = genLabel_urls(url);
	if(n0)
	{
		bReqValid = true;
		xmlAddChild(node, n0);
	}

	if(!bReqValid)
	{
		xmlFreeNode(node);
		return NULL;
	}
	else
	{
		return node;
	}
}

int parseLabel_rtsp(const char * url, xmlDocPtr doc)
{
	url = adjUrl(url, gLabelRtsp);
	if(NULL == url) return -1;

	bool bReqValid = false;
	if(0 == parseLabel_urls(url, doc))
	{
		bReqValid = true;
	}

	if(bReqValid)
	{
		return 0;
	}

	return -1;
}


/**
 * Context we keep for an upload.
 */
class CUploadContext
{
public:
	uint8_t * data;
	uint32_t  len;
	uint32_t  max;
};

static int
ahc_echo (void *cls,
		struct MHD_Connection *connection,
		const char *url,
		const char *method,
		const char *version,
		const char *upload_data, size_t *upload_data_size, void **ptr)
{
	struct MHD_Response *response;
	int ret = MHD_NO;

	xmlNodePtr node, n0;
	xmlDocPtr doc;
	xmlChar *xmlbuff;
	int buffersize;

	if(0 == strcmp(gMethodGet, method))
	{
		url = adjUrl(url, gUrlRootPath);

		if(NULL == url) return MHD_NO;

		doc = xmlNewDoc(BAD_CAST gXMLVersion );
		node = xmlNewNode(NULL, BAD_CAST gLabelConf);
		xmlDocSetRootElement(doc, node);

		bool bReqValid = false;

		n0 = genLabel_rtsp(url);
		if(n0)
		{
			xmlAddChild(node, n0);
			bReqValid = true;
		}

		xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);

		if(bReqValid)
		{
			response = MHD_create_response_from_buffer (buffersize,
					(void *) xmlbuff,
					MHD_RESPMEM_MUST_COPY);

			ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
			MHD_destroy_response (response);
		}

		xmlFree(xmlbuff);
		xmlFreeDoc(doc);
	}
	else if(0 == strcmp(gMethodPost, method))
	{
		CUploadContext *uc = (CUploadContext *)*ptr;
		if (NULL == uc)
		{
			uc = new CUploadContext;
			if(NULL == uc) return MHD_NO; /* out of memory, close connection */

			uc->len = 0;
			uc->max = 64*1000;
			uc->data= new uint8_t[uc->max];
			if(NULL == uc->data)
			{
				delete uc;
				return MHD_NO;
			}

			*ptr = uc;
			return MHD_YES;
		}
		if (0 != *upload_data_size)
		{
			if(uc->max - uc->len < *upload_data_size)
			{
				///copy data to new, free old
				uc->max = 2*uc->max;

				uint8_t * p = new uint8_t[uc->max];
				if(NULL == p)
				{
					delete[] uc->data;
					delete[] uc;
					return MHD_NO;
				}

				memcpy(p, uc->data, uc->len);

				delete[]uc->data;

				uc->data = p;
			}

			memcpy(uc->data+uc->len, upload_data, *upload_data_size);
			uc->len += *upload_data_size;

			*upload_data_size = 0;
			return MHD_YES;
		}


		doc = xmlReadMemory((const char*)uc->data, uc->len, "noname.xml", NULL, 0);
		delete[] uc->data;
		delete[] uc;

		if(NULL == doc) return MHD_NO;

		url = adjUrl(url, gUrlRootPath);
		if(NULL == url) return MHD_NO;

		bool bReqValid = false;
		if(0 == parseLabel_rtsp(url, doc))
		{
			bReqValid = true;
		}

		if(bReqValid)
		{
			response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_MUST_COPY);
			ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
			MHD_destroy_response (response);
		}

		xmlFreeDoc(doc);
	}

	return ret;
}


int
main (int argc, char *const *argv)
{
	///write the pid to the file
	string str(argv[0]);

	ostringstream ostr;
	ostr << getpid();


	ofstream fout( (str+".pid").c_str() );
	fout << ostr.str();
	fout.close();

	printf("pid:%s\n", ostr.str().c_str());

	struct MHD_Daemon *d;

	gTsUrl = new char[100];
	strcpy(gTsUrl, "rtsp://192.168.1.61/ts");

	if (argc != 2)
	{
		printf ("%s PORT\n", argv[0]);
		return 1;
	}
	d = MHD_start_daemon (// MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_POLL,
			MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | MHD_USE_POLL,
			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
			atoi (argv[1]),
			NULL, NULL, &ahc_echo,NULL,
			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
			MHD_OPTION_END);
	if (d == NULL)
		return 1;
	//(void) getc (stdin);
	while(1) {sleep(1);}
	MHD_stop_daemon (d);
	return 0;
}
